#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sikle.h"

void register_builtin_word(SikleState* ss, char* name, builtin_word_func bwf)
{
  NString ns = NString_new(name, strlen(name));

  Word word = {WT_BUILTIN, ns, bwf, 0};

  HashTable_put_word(WT(ss), word);
}

void _builtin_call(SikleState* ss)
{
  Word* word = (Word*)pop_ptr(ss);

  call_word(ss, word);
}

void _builtin_if(SikleState* ss)
{
  int condition;
  Word* word0 = NULL;
  Word* word1 = NULL;
  
  word0 = (Word*)pop_ptr(ss);
  word1 = (Word*)pop_ptr(ss);

  condition = pop_integer(ss);

  if(condition) {
    call_word(ss, word1);
  }
  else {
    call_word(ss, word0);
  }
}

void _builtin_while(SikleState* ss)
{
  int condition;
  Word* word = NULL;

  word = (Word*)pop_ptr(ss);  
  condition = peek_integer(ss);

  while(condition) {
    call_word(ss, word);

    condition = peek_integer(ss);
  }
}

void _builtin_gt(SikleState* ss)
{
  int i0, i1;

  i0 = pop_integer(ss);
  i1 = pop_integer(ss);

  push_integer(ss, i1 > i0);
}

void _builtin_lt(SikleState* ss)
{
  int i0, i1;

  i0 = pop_integer(ss);
  i1 = pop_integer(ss);

  push_integer(ss, i1 < i0);
}

void _builtin_ngt(SikleState* ss)
{
  int i0, i1;

  i0 = pop_integer(ss);
  i1 = pop_integer(ss);

  push_integer(ss, i1 <= i0);
}

void _builtin_nlt(SikleState* ss)
{
  int i0, i1;

  i0 = pop_integer(ss);
  i1 = pop_integer(ss);

  push_integer(ss, i1 >= i0);
}

void _builtin_add(SikleState* ss)
{
  int i0, i1;

  i0 = pop_integer(ss);
  i1 = pop_integer(ss);
  /* printf("%d + %d\n", i0, i1); */
  push_integer(ss, i0+i1);
}

void _builtin_sub(SikleState* ss)
{
  int i0, i1;

  i0 = pop_integer(ss);
  i1 = pop_integer(ss);
  
  push_integer(ss, i1-i0);
}

void _builtin_puts(SikleState* ss)
{
  NString ns = pop_string(ss);

  char* str = malloc(ns.size+1);
  strncpy(str, ns.data, ns.size);
  str[ns.size] = 0;
  printf("%s", str);
  free(str);
}

void _builtin_puti(SikleState* ss)
{
  int i0 = pop_integer(ss);

  printf("%d", i0);
}

void _builtin_swpi(SikleState* ss)
{
  int i0, i1;
  
  i0 = pop_integer(ss);
  i1 = pop_integer(ss);

  push_integer(ss, i0);
  push_integer(ss, i1);
}

void _builtin_swps(SikleState* ss)
{
  NString ns0, ns1;
  
  ns0 = pop_string(ss);
  ns1 = pop_string(ss);

  push_string(ss, ns0);
  push_string(ss, ns1);
}

void _builtin_mul(SikleState* ss)
{
  int i0, i1;

  i0 = pop_integer(ss);
  i1 = pop_integer(ss);
  
  push_integer(ss, i0*i1);
}

void _builtin_div(SikleState* ss)
{
  int i0, i1;

  i0 = pop_integer(ss);
  i1 = pop_integer(ss);
  
  push_integer(ss, i1/i0);
}

void _builtin_mod(SikleState* ss)
{
  int i0, i1;

  i0 = pop_integer(ss);
  i1 = pop_integer(ss);
  
  push_integer(ss, i0%i1);
}

void _builtin_dupi(SikleState* ss)
{
  int i0;

  i0 = pop_integer(ss);
  
  push_integer(ss, i0);
  push_integer(ss, i0);
}

void _builtin_dups(SikleState* ss)
{
  NString ns;

  ns = pop_string(ss);
  
  push_string(ss, ns);
  push_string(ss, ns);
}

void _builtin_dropi(SikleState* ss)
{
  pop_integer(ss);
}

void _builtin_drops(SikleState* ss)
{
  pop_string(ss);
}

void _builtin_drop_ptr(SikleState* ss)
{
  pop_ptr(ss);
}

void register_builtin_words(SikleState* ss)
{
  register_builtin_word(ss, "+", _builtin_add);
  register_builtin_word(ss, "-", _builtin_sub);
  register_builtin_word(ss, "*", _builtin_mul);
  register_builtin_word(ss, "/", _builtin_div);
  register_builtin_word(ss, "%", _builtin_mod);
  register_builtin_word(ss, "dupi", _builtin_dupi);
  register_builtin_word(ss, "dropi", _builtin_dropi);
  register_builtin_word(ss, "dups", _builtin_dups);
  register_builtin_word(ss, "drops", _builtin_drops);

  register_builtin_word(ss, "drop_ptr", _builtin_drop_ptr);

  register_builtin_word(ss, "puti", _builtin_puti);
  register_builtin_word(ss, "puts", _builtin_puts);
  register_builtin_word(ss, "swpi", _builtin_swpi);
  register_builtin_word(ss, "swps", _builtin_swps);

  register_builtin_word(ss, "call", _builtin_call);

  register_builtin_word(ss, ">", _builtin_gt);
  register_builtin_word(ss, ">=", _builtin_nlt);
  register_builtin_word(ss, "<", _builtin_lt);
  register_builtin_word(ss, "<=", _builtin_ngt);

  register_builtin_word(ss, "if", _builtin_if);
  register_builtin_word(ss, "repeat", _builtin_while);
}

